FCEUd, Tutorial #2 (Password Generators) [The Addams Family: Pugsley's Scavenger Hunt] by Parasyte of DES (http://www.dragoneyestudios.net/) v1.0 (4-30-03) -===========--- 1) Warm-Up -===============--- Before beginning, it's best to know what is needed in order to use this document to it's full potential. First, you must know the very basics of assembly programming. (The basics are simple, understand each opcode addressing mode, and each opcode type) You must also be well versed in binary, including binary math. Overall, not much is needed to start using FCEUd. For 6502 assembly documents and tutorials, check out Zophar's Domain and ROM Hacking.com at the URLs below. As an alternative, search for "6502 Assembly" in everyone's favorite search engine - Google. Things you'll need: FCEUd (get from http://www.dragoneyestudios.net/) The Addams Family: Pugsley's Scavenger Hunt ROM 6502 ASM docs, available from: http://www.zophar.net/ http://www.obelisk.demon.co.uk/6502/reference.html http://www.romhacking.com/ -===========--- 2) Finding The Password -==============--- The first thing you need to do when hacking password routines is finding the entered password in RAM! This only requires a simple cheat search. Just open the ROM, and go into the password-entry screen. From here, enter a single "1" character (you must push the A button to actually enter the character). Then open the FCEUd Cheats window from NES -> Cheats... With the cheats window open, click Add Cheat to open the Cheat Console. In here, you're presented with a cheat search, this is what we will use to find the entered password in RAM. When starting a cheat search, the first thing you must do is push the Reset Search button. After that, close all the cheat windows to get back to the game. Next, erase the entered "1" character with the B button, and enter a "2." Get back to the cheat search and set the Filter as "O!=C," which means "Original Value is not equal to Current Value." Push Do Search, then push Set Original To Current, and get back to the game. Now enter about three "1" characters, so the entered password is now "2111." Go back to the cheat search, And select the "|O-C|==V2" filter. This means "The Difference Between Original and Current is equal to V2." V2 is the text box to the left of the filters, you can't miss it! Make sure V2 contains 0, because that first password character has not changed since the last search. Push Do Search! Once again, go back to the game and enter "Z" as the first password character. Now, back in the cheat search, the value has changed, so filter the search with "O!=C." Push Do Search! Repeat the search process until you get one possible address. I'll just cheat a bit and tell you the address is $043D. -===========--- 3) Finding The Password Verifier -==============--- Now we know where the entered password is held in RAM, we just need to use this knowledge to find the password verifier routine. The routine which will ensure the password is not just randomly entered junk! Open FCEUd's Debug Console with NES -> Debug, or by pushing F1. Then get back to the game, and enter a random password, such as "2211B" But, before entering the last character in the password, set a breakpoint on read (BPR) on address $043D. And we're shown that the debugger snaps immediately, so we have to work around this. Disable the first BPR before continuing. With the debugger snapped, set a breakpoint on write (BPW) on $0441, which just so happens to be the address which holds the last password character. Finally, enter your last password character and the debugger will snap thanks to the second breakpoint. You can delete that one now, as it serves no more purpose. Enable the first breakpoint you set, and hit the Run button! You will be placed right in the password decoding routine, the one you will need to rip in order to write your generator! In the disassembler window, scroll up until you come across an RTS opcode. Luckily it's on the line directly above the PC address. This means the PC is on the start of the routine. So copy all the ASM starting from the beginning of the routine, and ending at the first RTS you come scross. Just paste it all into notepad for reverse engineering! -===========--- 4) Translating 6502 to C or another Programming Language -==============--- This is the fun part, making the actual password generator! The first thing I do at this point is take a quick look at the ripped assembly. I'm seeing the familiar addresses which contain the entered password, this is a good thing. Let's take a look at the routine in small pieces, now. $BAC8:AD 3D 04 LDA $043D = #$01 $BACB:18 CLC $BACC:6D 3E 04 ADC $043E = #$01 $BACF:18 CLC $BAD0:6D 3F 04 ADC $043F = #$00 $BAD3:18 CLC $BAD4:6D 40 04 ADC $0440 = #$00 $BAD7:18 CLC $BAD8:69 25 ADC #$25 $BADA:29 1F AND #$1F $BADC:CD 41 04 CMP $0441 = #$09 $BADF:D0 3A BNE $BB1B This is extremely simple code, it loads the first password character from $043D, then adds that value to the next three password characters. This is the most basic of all checksums, but this game does something a bit different. After generating the checksum, it adds $25 to the value, just to offset the checksum a bit. Finally, it masks the checksum with $1F. (The highest possible password character value) Then it simply compares the final checksum to the last password character. If they do not match, it branches to $BB1B. Checking your ripped ASM, make sure you've got $BB1B, and you wouldn't have if you did stop copying at the first encounted RTS opcode. $BB1B contains a very small amount of code, which simply returns with a value of -1, for error. $BB1B:A9 FF LDA #$FF $BB1D:60 RTS Nothing to it! So, with all of this in mind, let's write some code to do exactly what is done above. I'll be using C, as it's my favorite programming language. u8 password[5]; //unsigned character array to hold entered password chksum = ((password[0] + password[1] + password[2] + password[3] + 0x25) & 0x1F); if (password[4] != chksum) return -1; Quite simple, isn't it? Next we'll check out the next piece of ASM, which starts to get a bit tricky. $BAE1:AD 3D 04 LDA $043D = #$01 $BAE4:49 0A EOR #$0A $BAE6:0A ASL $BAE7:0A ASL $BAE8:0A ASL $BAE9:0A ASL $BAEA:8D 3B 04 STA $043B = #$00 $BAED:AD 3E 04 LDA $043E = #$01 $BAF0:49 0F EOR #$0F $BAF2:4A LSR $BAF3:0D 3B 04 ORA $043B = #$00 $BAF6:8D 3B 04 STA $043B = #$00 $BAF9:29 02 AND #$02 $BAFB:D0 1E BNE $BB1B The first password character is loaded, then it's XOR'd with $0A, and shifted left by four. Next it's temporarily stored away, and the second password character is loaded, then XOR'd with $0F. Finally, that value is shifted right by one, then it's OR'd with the previously decoded value. There's also a little check which ensures bit 1 is unset. If the bit is set, the routine returns with an error. u8 decode[3]; //buffer for decoded data decode[2] = (((password[0] ^ 0x0A) << 4) | ((password[1] ^ 0x0F) >> 1)); if (decode[2] & 0x02) return 1; You'll see why I used an array in a moment. I usually run through these routines a few times before translating any of it. The next pieces of the routine are a lot simpler, and will require a lot less work. $BAFD:AD 3F 04 LDA $043F = #$00 $BB00:49 05 EOR #$05 $BB02:8D 37 04 STA $0437 = #$00 Load password character 3, XOR with $05, store it into the buffer! decode[1] = (password[2] ^ 0x05); See?! So Simple!! $BB05:AD 40 04 LDA $0440 = #$00 $BB08:49 02 EOR #$02 $BB0A:4A LSR $BB0B:8D 38 04 STA $0438 = #$05 Load password character 4, XOR with $02, shift right one, store into buffer! decode[0] = ((password[3] ^ 0x02) >> 1); The rest of the routine is mostly useless, so you can easily ignore it. All it does is fill some memory with certain values. -===========--- 5) Putting It All Together -==============--- The last thing to do is put it all together into a program, and you have a fully funtional password verifier. Now that's great and all, but you want the program to generate passwords, not just verify them! How do we do this with the code we wrote? In 9 of 10 cases, the answer is "REVERSE IT!" That is, take your code, and rewrite it to do the exact opposite. So, for reference, here is the completed verifier function. u8 VerifyPass(u8 *password, u8 *buffer) { int chksum = ((password[0]+password[1]+password[2]+password[3]+0x25)&0x1F); if (password[4] != chksum) return -1: buffer[2] = (((password[0]^0x0A)<<4)|((password[1]^0x0F)>>1)); if (buffer[2]&0x02) return -1; buffer[1] = (password[2]^0x05); buffer[0] = ((password[3]^0x02)>>1); return 0; } Now reverse the operations for a generator function. u8 GeneratePass(u8 *password, u8 *buffer) { if (buffer[2]&0x02) return -1; password[0] = ((buffer[2]>>4)^0x0A); password[1] = (((buffer[2]<<1)^0x0F)&0x1F); password[2] = (buffer[1]^0x05); password[3] = ((buffer[0]<<1)^0x02); password[4] = ((password[0]+password[1]+password[2]+password[3]+0x25)&0x1F); return 0; } I hope you understand how I reversed the operations. Basically, do everything in reverse, with opposites. For example, add instead of subtract. Also notice the masking I had to do for password[1]. This code just prevents the value from going above $1F, which is the highest possible value for a password character. Now you just put it together into a nice program, and you're done! -===========--- 6) Understanding The Decoded Data -==============--- The secret to making your password generator useful is by understanding the decoded password data. All I did was compare the data in the buffer to the items and such I was given. To keep this section short, buffer[0] is the upper digit of your lives counter, buffer[1] is the lower digit for lives, and finally, buffer[2] contains your number of hearts, and each family member rescued. -===========--- 7) Legal Information -===============--- This document is Copyright 2003 Parasyte\Dragon Eye Studios. This doument may not be modified in any way, in part or whole without permission of the author. Parasyte and Dragon Eye Studios are in no way affiliated with Nintendo. Addams Family et.al and Nintendo are registered trademarks of thier respected owners. Parasyte and Dragon Eye Studios are not responsible for how you use the information contained in this document. We are not responsible for the fact that it may eat your homework and your dog. Please remember to wipe. EOF